﻿using CasignDesignGA.Casing;
using CasignDesignGA.Engine;
using CasignDesignGA.Engine.Mutation;
using CasignDesignGA.Engine.Population;
using CasignDesignGA.Engine.Reproduction;
using CasignDesignGA.Engine.Selection;
using CasignDesingGA.Casing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CasignDesignGA.Engine
{
    public class GAEngine : IGAEngine
    {
        private Population.Population currentPopulation;

        internal Population.Population CurrentPopulation
        {
            get { return currentPopulation; }
            set { currentPopulation = value; }
        }

        private double designFactor = 0;

        public double DesignFactor
        {
            get { return designFactor; }
            set { designFactor = value; }
        }


        private long currentGeneration;

        public long CurrentGeneration
        {
            get { return currentGeneration; }
            set { currentGeneration = value; }
        }
        private long initialPopulation;

        public long InitialPopulation
        {
            get { return initialPopulation; }
            set { initialPopulation = value; }
        }
        private long maxGenerations;

        public long MaxGenerations
        {
            get { return maxGenerations; }
            set { maxGenerations = value; }
        }
        private long maxLivingTime;

        public long MaxLivingTime
        {
            get { return maxLivingTime; }
            set { maxLivingTime = value; }
        }

        private double mutationFactor;

        public double MutationFactor
        {
            get { return mutationFactor; }
            set { mutationFactor = value; }
        }

        private long crossingSiblings;

        public long CrossingSiblings
        {
            get { return crossingSiblings; }
            set { crossingSiblings = value; }
        }

        public GAEngine(long initialPopulation, long maxGenerations, long maxLivingTime, long crossingSiblings, double mutationFactor, double designFactor)
        {
            this.currentGeneration = 0L;
            this.InitialPopulation = initialPopulation;
            this.MaxGenerations = maxGenerations;
            this.maxLivingTime = maxLivingTime;
            this.mutationFactor = mutationFactor;
            this.CrossingSiblings = crossingSiblings;
            this.designFactor = designFactor;

        }


        private void generateInitialPopulation()
        {
            IPopulationFactory aPopulationFactory = new CatalogPopulationFactory();
            
            Population.Population initialPopulationList = aPopulationFactory.generatePopulation(this.InitialPopulation, this.designFactor);


            currentPopulation = initialPopulationList;
        }

        public bool start()
        {
            this.generateInitialPopulation();

            return true;
        }

        public void reset()
        {
            
            this.currentGeneration = 0;
            this.currentPopulation = null;
        }

        public void step()
        {
           
           
           Population.Population crossingSiblings = EliteRouletteSelectionAlgorithm.Instance.selectPopulation(this.CurrentPopulation, (int)CrossingSiblings);

           Population.Population newSiblings = CrossOverAlgorithm.Instance.performCrossover(crossingSiblings);

           this.mutateSiblings(newSiblings);
           
           this.CurrentPopulation.Add(newSiblings);
           this.CurrentPopulation.AgePopulation((int)this.maxLivingTime);
           this.CurrentPopulation.ReCalculateAptitude(this.designFactor);
           this.CurrentGeneration++;
           
                  
        }
        private void mutateSiblings(Population.Population newSiblings)
        {
            Random rand = new Random(DateTime.Now.Millisecond);
            /*Mutate siblings*/
            for (int i = 0; i < newSiblings.count(); i++)
            {
                if (rand.NextDouble() < this.MutationFactor)
                {
                    ISelectable mutateIndividual = newSiblings.getIndividualIndex(i);
                    int mutationAlgorithm = rand.Next(2);
                    switch (mutationAlgorithm)
                    {
                        case 0: mutateIndividual = InversionMutationAlgorithm.Instance.mutate(mutateIndividual);
                            break;
                        case 1: mutateIndividual = LongitudeMutationAlgorithm.Instance.mutate(mutateIndividual);
                            break;
                        case 2: mutateIndividual = RandomMutationAlgorithm.Instance.mutate(mutateIndividual);
                            break;
                        default: break;

                    }
                }

            }
        }
    }
   
}
